package redecolaborativa.server.gerentes;


import java.io.Serializable;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;



import redecolaborativa.server.excecoes.*; 
import redecolaborativa.server.objetos.*;
import redecolaborativa.server.sistema.Usuario;


public class GerenciadorDeEmprestimos implements Serializable {
	private static final long serialVersionUID = 1L;
	private List<Emprestimo> emprestimos, solicitacoesDeEmprestimo, adquiridos, historicoEmprestimos;
	private List<Item> requisicoesDeDevolucao, notificacoesDeDevolucao;
	
	public GerenciadorDeEmprestimos() {
		emprestimos = new LinkedList<Emprestimo>();
		solicitacoesDeEmprestimo = new LinkedList<Emprestimo>();
		adquiridos = new LinkedList<Emprestimo>();
		historicoEmprestimos = new LinkedList<Emprestimo>();
		requisicoesDeDevolucao = new LinkedList<Item>();
		notificacoesDeDevolucao = new LinkedList<Item>();
	}
	
	/**
	 * Retorna a copia da lista de emprestimos realizados pelo usuario.
	 * @return Acopia da lista de emprestimos realizados pelo usuario.
	 */
	public List<Emprestimo> getEmprestimos() {
		return new LinkedList<Emprestimo>(emprestimos);
	}
	
	/**
	 * Recupera uma copia das solicitacoes de emprestimo de um usuario
	 * @return Copia das solicitacoes de emprestimo de um usuario
	 */
	public List<Emprestimo> getSolicitacoesDeEmprestimo() {
		return new LinkedList<Emprestimo>(solicitacoesDeEmprestimo);
	}
	
	/**
	 * Recupera uma copia da lista de itens adquiridos por um usuario
	 * @return Uma copia da lista de itens adquiridos por um usuario
	 */
	public List<Emprestimo> getAdquiridos() {
		return new LinkedList<Emprestimo>(adquiridos);
	}
	
	/**
	 * Retorna o historico de emprestimos realizados do usuario.
	 * @return O historico de emprestimos realizados do usuario.
	 */
	public  List<Emprestimo> getHistoricoEmprestimos() {
		return new LinkedList<Emprestimo>(historicoEmprestimos);
	}
	
	/**
	 * Recupera uma copia da lista de notificacoes de devolucao de um item
	 * @return Uma copia da lista de notificacoes de devolucao de um item
	 */
	public List<Item> getNotificacoesDeDevolucao() {
		return new LinkedList<Item>(notificacoesDeDevolucao);
	}
	
	/**
	 * Recupera um copia da lista de requisicoes de devolucao de um item
	 * @return Uma copia da lista de requisicoes de devolucao de um item
	 */
	public List<Item> getRequisicoesDeDevolucao() {
		return new LinkedList<Item>(requisicoesDeDevolucao);
	}
	
	/**
	 * Metodo que pede emprestado um item
	 * @param usuario Usuario que pede emprestado
	 * @param item Item ao qual quer emprestado
	 * @param tempoPrevisto Tempo de emprestimo
	 * @throws BeneficiadoInvalidoException
	 * @throws ItemInvalidoException
	 * @throws NumeroNegativoException
	 * @throws ItemIndisponivelException 
	 * @throws PermissaoNegadaException 
	 * @throws RequisicaoInvalidaException 
	 */
	public void pedeItemEmprestado(Usuario usuario, Item item, int tempoPrevisto) throws BeneficiadoInvalidoException, ItemInvalidoException, NumeroNegativoException, ItemIndisponivelException, PermissaoNegadaException, RequisicaoInvalidaException {
		if (!item.estaDisponivel()) {
			throw new ItemIndisponivelException("Esse item nao esta disponivel.");
		} if (!item.getDono().gerenteDeAmigos().contem(usuario)) {
			throw new PermissaoNegadaException("O usuário não tem permissão para requisitar o empréstimo deste item");
		} if (jaRequisitouItem(item)) {
			throw new RequisicaoInvalidaException("Requisição já solicitada") ;
		}
		item.removeInteressado(usuario);
		Emprestimo emprestimo = new Emprestimo(usuario, item, tempoPrevisto);
		item.getDono().gerenteDeEmprestimos().solicitacoesDeEmprestimo.add(emprestimo); 
		emprestimo.geraIDRequisicao();
	}
	
	/**
	 * Metodo que aceita a solicitaca de emprestimo de um item
	 * @param usuario Usuario que quer o item emprestado
	 * @param item Item a ser emprestado
	 * @throws EmprestimoInvalidoException
	 * @throws TipoDeMensagemInvalido
	 * @throws AssuntoInvalidoException
	 * @throws DescricaoInvalidaException
	 * @throws InterruptedException
	 */
	public void aceitaSolicitacaoDeItem(Usuario usuario, Item item) throws EmprestimoInvalidoException, TipoDeMensagemInvalido, AssuntoInvalidoException, DescricaoInvalidaException, InterruptedException {
		Iterator<Emprestimo> it = solicitacoesDeEmprestimo.iterator();
		while (it.hasNext()){
			Emprestimo emp = it.next();
			if (emp.getItem().equals(item)) {
				if (!usuario.gerenteDeAmigos().contem(emp.getBeneficiado())){
					throw new EmprestimoInvalidoException("Requisição de empréstimo inexistente");
				} if (usuario.gerenteDeEmprestimos().getEmprestimo(item) != null){
					throw new EmprestimoInvalidoException("Empréstimo já aprovado");
				}
				emp.iniciar();
				emp.getItem().setDevolucao(false); emp.setTermino(false);
				emp.getBeneficiado().gerenteDeEmprestimos().adquiridos.add(emp);
				adiciona(emp); 
			}
		}
	}
		
	/**
	 * Rejeita solicitacao de emprestimo do item, removendo o emprestimo da lista de solicitacao de emprestimo do usuario. 
	 * @param Item a ser rejeitado
	 */
	public void rejeitaSolicitacaoDeItem(Item item) {
		Iterator<Emprestimo> it = solicitacoesDeEmprestimo.iterator();
		while (it.hasNext()){
			Emprestimo emp = it.next();
			if (emp.getItem().equals(item)){
				it.remove();
			}
		}
	}
	
	/**
	 * Metodo que requisita a devolucao de um item
	 * @param item Item a ser devolvido
	 * @return O emprestimo
	 * @throws RequisicaoInvalidaException
	 */
	public Emprestimo requisitaDevolucaoDeItem(Item item) throws RequisicaoInvalidaException {
		Emprestimo emprestimo = getEmprestimo(item);
		if (emprestimo == null || item.foiDevolvido()) {
			throw new RequisicaoInvalidaException("Item já devolvido");
		} if (emprestimo.getBeneficiado().gerenteDeEmprestimos().requisicoesDeDevolucao.contains(item)) {
			throw new RequisicaoInvalidaException("Devolução já requisitada");
		}
		emprestimo.verificaCancelado();
		emprestimo.getBeneficiado().gerenteDeEmprestimos().requisicoesDeDevolucao.add(item);
		return emprestimo;
	}
	
	/**
	 * Notifica o dono do item que se quer devolver o item, adicionando o item na lista de notificacoes de devolucoes.
	 * @param Item a ser devolvido 
	 * @throws ItemInvalidoException 
	 */
	public void devolveItem(Item item) throws ItemInvalidoException  {
		if(item.foiDevolvido()) {
			throw new ItemInvalidoException("Item já devolvido");
		}
		Iterator<Emprestimo> it = adquiridos.iterator();
		while (it.hasNext()){
			Emprestimo emp = it.next();
			if (emp.getItem().equals(item)){
				item.setDevolucao(true);
				item.getDono().gerenteDeEmprestimos().notificacoesDeDevolucao.add(item);
				requisicoesDeDevolucao.remove(item);
			}
		}
	}
	
	/**
	 * Confirma a devolucao do item, finalizando o emprestimo e removendo o item da lista de notificacoes de devolucoes.
	 * @param Item a ser confirmado a devolucao
	 * @throws EmprestimoInvalidoException 
	 * @throws DescricaoInvalidaException 
	 * @throws AssuntoInvalidoException 
	 * @throws TipoDeMensagemInvalido 
	 * @throws InterruptedException 
	 */
	public void confirmaDevolucao(Item item) throws EmprestimoInvalidoException, TipoDeMensagemInvalido, AssuntoInvalidoException, DescricaoInvalidaException, InterruptedException {
		Emprestimo emprestimo = getEmprestimo(item);
		if (emprestimo == null || emprestimo.foiTerminado()) {
			throw new EmprestimoInvalidoException("Término do empréstimo já confirmado");
		}
		emprestimo.setTermino(true);
		emprestimo.finalizar(); 
		solicitacoesDeEmprestimo.remove(emprestimo); 
		notificacoesDeDevolucao.remove(item);
		emprestimos.remove(emprestimo);	
	}
	
	/**
	 * Recupera um emprestimo atraves de um item especificado.
	 * @param Item a ser procurado no emprestimo
	 * @return O emprestimo relacionado ao item, ou null caso nao tenha o item emprestado
	 */
	public Emprestimo getEmprestimo(Item item) {
		Iterator<Emprestimo> it =  emprestimos.iterator();
		while (it.hasNext()){
			Emprestimo emp = it.next();
			if (emp.getItem().equals(item)) {
				return emp;
			}
		} 
		return null;
	}
	
	/**
	 * Contabiliza a quantidade de emprestimos que o usuario realizou.
	 * @return A quantidade de emprestimos
	 */
	public int emprestimosFinalizados() {
		int finalizados = 0;
		Iterator<Emprestimo> it = historicoEmprestimos.iterator();
		while (it.hasNext()){
			Emprestimo emp = it.next();
			if (!emp.canceladoAntes()){
				finalizados++;
			}
		}
		return finalizados;
	}
	
	/**
	 * Acrescenta dias em um emprestimo	
	 * @param dias Quantidade de dias
	 */
	public void acrescentaDias(int dias) {
		Iterator<Emprestimo> it = emprestimos.iterator();
		while (it.hasNext()){
			Emprestimo emp = it.next();
			if (emp.getDataFinal() != null) {
				emp.getDataFinal().add(Calendar.DAY_OF_YEAR, dias);
			}
		}
	}
	
	/*
	 * Adiciona um emprestimo a lista de emprestimos do usuario.
	 * @param Emprestimo a ser adicionado
	 * @return True caso o emprestimo for adicionado ou False caso o emprestimo nao for adicionado
	 * @throws InterruptedException 
	 */
	public void adiciona(Emprestimo emprestimo) throws InterruptedException {
		Thread.sleep(1);
		Usuario emprestador = emprestimo.getItem().getDono(); 
		if (!emprestimos.contains(emprestimo)) {
			emprestimos.add(emprestimo);
			historicoEmprestimos.add(emprestimo);
			String atividade = String.format("%s emprestou %s a %s", emprestador.getNome(), emprestimo.getItem().getNome(), emprestimo.getBeneficiado().getNome());
			emprestador.getHistoricoDeAtividades().adicionaAtividade(atividade, new GregorianCalendar());
		}
	}
	
	/*
	 * Verifica se um usuario ja requisitou dado item.
	 */
	private boolean jaRequisitouItem(Item item) {
		Iterator<Emprestimo> it = item.getDono().gerenteDeEmprestimos().solicitacoesDeEmprestimo.iterator();
		while (it.hasNext()){
			Emprestimo emp = it.next();
			if (emp.getItem().equals(item)) {
				return true;
			}
		}
		return false;
	}
	
	/**
	 * Visualiza os itens adquiridos pelo usuario
	 * @param idSessaoAberta Id da sessao do usuario
	 * @throws SessaoInvalidaException
	 */
	public String retornaItensAdquiridos() throws SessaoInvalidaException{
		String retorno = " ";
		if(adquiridos.isEmpty()) {
			retorno = "Nenhum emprestimo realizado.";
		} else {
			for(Emprestimo emprestimo : adquiridos) {
				retorno += String.format("Item: %s | Id do emprestimo: %s \n", emprestimo.getItem().getNome(), emprestimo.getIDEmprestimo());
			}
		}
		return retorno;
	}	
}